﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace UniSender
{
    using IValues = IDictionary<string, string>;

    public static class Parser
    {
        private static IDictionary<string, T> Combine<T>(string[] fields, T[] data)
        {
            var values = new Dictionary<string, T>();
            for (int i = 0; i < data.Length; ++i)
                values[fields[i]] = data[i];
            return values;
        }

        private static void Handle<T>(IDictionary<string, T> values, string key, Action<T> handler)
        {
            if (values.ContainsKey(key))
            {
                handler(values[key]);
                values.Remove(key);
            }
        }

        #region Parse Export Results

        public static Subscriber[] ParseExport(Native.ExportResult result)
        {
            return result == null ? null : result.data
                .Select(values => ParseSubscriber(Combine(result.field_names, values)))
                .ToArray();
        }

        public static Subscriber ParseSubscriber(IValues values)
        {

            var contact = new Subscriber();

            Handle(values, "tags", value => contact.Tags = value.Split(','));
            Handle(values, "email", value => {
                Deserialize(values, contact.Email = new EmailContact(value));
                Handle(values, "email_confirm_time", v => contact.Email.ConfirmTime = Utility.ParseTime(v));
                Handle(values, "email_confirm_ip", v => contact.Email.ConfirmIP = v);
            });
            Handle(values, "phone", value =>
                Deserialize(values, contact.Phone = new PhoneContact(value))
            );
            contact.Custom = values;

            return contact;
        }

        public static void Deserialize(IValues values, Contact contact)
        {
            string type = contact is EmailContact ? "email_" : "phone_";
            Handle(values, type + "status", value => contact.Status = Utility.ParseEnum<ContactStatus>(value));
            Handle(values, type + "availability", value => contact.Availability = Utility.ParseEnum<ContactAvailability>(value));
            Handle(values, type + "add_time", value => contact.AddTime = Utility.ParseTime(value));
            Handle(values, type + "request_ip", value => contact.RequestIP = value);
            Handle(values, type + "list_ids", slists => {
                var lists = slists.Split(',')
                    .Select(s => long.Parse(s))
                    .ToArray();
                var times = values[type + "subscribe_times"].Split(',')
                    .Select(s => Utility.ParseTime(s))
                    .ToArray();
                contact.Subscriptions = lists
                    .Select((listId, i) => new Subscription(listId, times[i]))
                    .ToArray();
            });
            Handle(values, type + "unsubscribed_list_ids", value =>
                contact.UnsubscribedListIds = value.Split(',').Select(s => long.Parse(s)).ToArray()
            );
            Handle(values, type + "excluded_list_ids", value =>
                contact.ExcludedListIds = value.Split(',').Select(s => long.Parse(s)).ToArray()
            );
        }

        #endregion

        #region Parse Delivery Stats

        public static CampaignStats[] ParseDeliveriesStats(Native.CampaignStats stats)
        {
            return stats.data
                .Select(values => ParseDeliveryStats(Combine(stats.fields, values)))
                .ToArray();
        }

        public static CampaignStats ParseDeliveryStats(IValues values)
        {
            var result = new CampaignStats();
            Handle(values, "phone", value => result.Phone = value);
            Handle(values, "email", value => result.Phone = value);
            Handle(values, "send_result", value => result.Status = Utility.ParseEnum<MessageStatus>(value));
            Handle(values, "last_update", value => Utility.ParseTime(value));
            result.Custom = values;
            return result;
        }

        #endregion

        #region Parse Links Stats

        public static LinkStats[] ParseLinksStats(Native.LinksStats stats)
        {
            return stats.data
                .Select(values => ParseLinkStats(Combine(stats.fields, values)))
                .ToArray();
        }

        public static LinkStats ParseLinkStats(IDictionary<string, object> values)
        {
            var result = new LinkStats();
            Handle(values, "email", value => result.Email = (string)value);
            Handle(values, "url", value => result.Url = (string)value);
            Handle(values, "request_time", value => result.RequestTime = Utility.ParseTime((string)value));
            Handle(values, "ip", value => result.IP = (string)value);
            Handle(values, "count", value => result.Count = (int)value);
            return result;
        }

        #endregion
    }
}
